/**
 * This class represents both the Events Calendar and Sessional Dates
 * modules of the iWestern application. 
 * @author Chirag Mistry
 */
package ca.uwo.android.rss.events;

import java.io.IOException;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Vector;

import org.xml.sax.SAXException;

import android.app.AlertDialog;
import android.app.ExpandableListActivity;
import android.app.SearchManager;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.Configuration;
import android.net.Uri;
import android.os.Bundle;
import android.provider.SearchRecentSuggestions;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ExpandableListView;
import android.widget.ExpandableListView.OnChildClickListener;
import android.widget.ImageButton;
import android.widget.SimpleExpandableListAdapter;
import android.widget.TextView;
import ca.uwo.android.R;
import ca.uwo.android.mustangs.MustangsScheduleParser;
import ca.uwo.android.rss.RssFeed;
import ca.uwo.android.rss.RssItem;
import ca.uwo.android.rss.SaxEventCalendarParser;
import ca.uwo.android.rss.SaxRssParser;

public class RssDatesFeed extends ExpandableListActivity {

	/**
	 * Used to tag log messages from this class
	 */
	private static final String TAG_NAME = "RssDatesFeed";

	/**
	 * constants to determine which type of dates feed is to be parsed
	 */
	public static final int EVENTSCALENDAR = 0;
	public static final int SESSIONALDATES = 1;
	public static final int SERVICEINTERRUPTIONS = 2;
	public static final int ALUMNIEVENTS = 3;
	public static final int MUSTANGSCHEDULE = 4;

	/**
	 * url of the clicked feed item
	 */
	private String url;

	/**
	 * set as one of the above constants
	 */
	private int feedType;

	/**
	 * The title to show in the module header
	 */
	private String datesFeedHeader;

	/**
	 * stores the rss feed of the url
	 */
	private RssFeed fullRssFeed;

	/**
	 * The currently-displayed rss feed
	 */
	private RssFeed displayedRssFeed;

	/**
	 * stores the events / deadlines
	 */
	private Vector<Vector<String>> displayedEvents;

	/**
	 * Handle configuration changes ourselves. Implementing this method and
	 * adding android:configChanges="keyboardHidden|orientation" to the manifest
	 * for this activity means that the onCreate method will not be called every
	 * time a user rotates their phone.
	 */
	public void onConfigurationChanged(Configuration newConfig) {
		super.onConfigurationChanged(newConfig);
	}

	/**
	 * Called when the activity is first created. Or when restarted after
	 * reclaimed for memory.
	 */
	@Override
	public void onCreate(Bundle savedInstanceState) {

		super.onCreate(savedInstanceState);
		setContentView(R.layout.datesfeedmain);
		setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL);

		// Log.i(TAG_NAME, "In onCreate, savedInstanceState == null?  " +
		// (savedInstanceState == null));
		// Log.i(TAG_NAME, "In onCreate, datesFeedHeader = " + datesFeedHeader);

		// get the url and type of date feed to be parsed
		url = getIntent().getExtras().getString("URL");
		feedType = getIntent().getExtras().getInt("TYPE");
		datesFeedHeader = getIntent().getExtras().getString("HEADER");

		//Log.i(TAG_NAME, "about to parse feed");
		fullRssFeed = parseRssFeed();
		//Log.i(TAG_NAME, "parsed feed");

		// //add all dates into Content provider for searching
		// ContentResolver res = (EventContentProvider)getContentResolver();
		//		
		// getContentResolver().load(EventContentProvider.CONTEN_URI,
		// fullRssFeed);
		//		
		//Log.i(TAG_NAME, "about tot display feed");
		displayRssFeed(fullRssFeed);
		//Log.i(TAG_NAME, "finished displaying feed");

		((TextView) findViewById(R.id.moduleName)).setText(datesFeedHeader);

		ExpandableListView exListView = getExpandableListView();

		exListView.setOnChildClickListener(new OnChildClickListener() {

			/**
			 * This method is used to redirect the user to the correct summary
			 * screen when the user clicks an item in the list.
			 * 
			 * @param parent
			 *            The expandable list view from which the item was
			 *            clicked
			 * @param v
			 *            The text view pertaining to clicked item
			 * @param groupPosition
			 *            The index of the group that event belongs to
			 * @param childPosition
			 *            The index of the event
			 */
			@Override
			public boolean onChildClick(ExpandableListView parent, View v,
					int groupPosition, int childPosition, long id) {

				// get the index of the clicked item in the list
				int rssIndex = getItemIndex(displayedEvents, groupPosition,
						childPosition);

				// get the appropriate item from the stored rss feed
				RssItem clickedItem = displayedRssFeed.get(rssIndex);

				Intent showSummary = null;

				if (feedType == MUSTANGSCHEDULE) {
					showSummary = new Intent(RssDatesFeed.this,
							ca.uwo.android.mustangs.MustangsDateItemSummary.class);

					// pass the information required to populate fields in
					// summary
					// screen
					showSummary.putExtra("DATE", clickedItem.getDate());
					showSummary.putExtra("TITLE", clickedItem.getTitle());
					showSummary.putExtra("RESULTS", clickedItem
							.getDescription());
					showSummary.putExtra("LOCATION", clickedItem
							.getLocation());
					showSummary.putExtra("LINK", clickedItem
							.getLink());
					showSummary.putExtra("TYPE", feedType);
				} else {
					showSummary = new Intent(RssDatesFeed.this,
							ca.uwo.android.rss.events.DateItemSummary.class);

					// pass the information required to populate fields in
					// summary
					// screen
					showSummary.putExtra("DATE", clickedItem.getDate());
					showSummary.putExtra("TITLE", clickedItem.getTitle());
					showSummary.putExtra("SUMMARY", clickedItem
							.getDescription());
					showSummary.putExtra("TYPE", feedType);

				}
				startActivity(showSummary); // display the summary screen to the
				// user
				return true;
			}
		});

		// Search ButtonFound key
		((ImageButton) findViewById(R.id.searchButton))
				.setOnClickListener(new OnClickListener() {
					@Override
					public void onClick(View v) {
						onSearchClicked();
					}
				});

		// Clear button
		ImageButton clearButton = (ImageButton) findViewById(R.id.clearButton);
		clearButton.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				handleClear(v);
			}
		});
		// set clear button hidden at first
		clearButton.setVisibility(View.GONE);

		handleIntent(getIntent());

	}

	@Override
	protected void onNewIntent(Intent intent) {
		setIntent(intent);
		handleIntent(intent);
	}

	/**
	 * Handle any new intents. Currently, only a Search intent is handled.
	 * 
	 * @param intent
	 *            the intent to be handled
	 */
	private void handleIntent(Intent intent) {
		if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
			String query = intent.getStringExtra(SearchManager.QUERY);
			SearchRecentSuggestions suggestions = new SearchRecentSuggestions(
					this, SuggestionProvider.AUTHORITY, SuggestionProvider.MODE);
			suggestions.saveRecentQuery(query, null);
			handleSearch(query);
		}
	}

	/**
	 * Search for the given string and redisplay results matching search query
	 * 
	 * @param query
	 *            the keywords to search
	 */
	private void handleSearch(String query) {

		ImageButton searchButton = (ImageButton) findViewById(R.id.searchButton);

		ImageButton clearButton = (ImageButton) findViewById(R.id.clearButton);
		clearButton.setVisibility(View.GONE);

		RssFeed foundItems = null;

		// Log.i(TAG_NAME, "In handle search intent with query " + query);

		// if the user does not want to search for anything
		if (query == null || query.trim().length() == 0) {
			displayRssFeed(fullRssFeed);
			searchButton.setVisibility(View.VISIBLE);
			clearButton.setVisibility(View.GONE);
		}

		else {
			foundItems = searchRssFeed(fullRssFeed, query);
			// for (int i = 0; i < foundItems.size(); i++) {
			// Log.i(TAG_NAME, "\tFound item:  "
			// + foundItems.get(i).getTitle());
			// }
			if (foundItems.size() == 0) {

				showAlert(
						"No items containing keywords [" + query + "] found.",
						false);
				searchButton.setVisibility(View.VISIBLE);
				clearButton.setVisibility(View.GONE);

			} else {
				displayRssFeed(foundItems);

				searchButton.setVisibility(View.GONE);
				clearButton.setVisibility(View.VISIBLE);
			}
		}

	}

	/**
	 * Search for the given string and redisplay results matching search query
	 * 
	 * @param v
	 * 
	 * @param query
	 *            the keywords to search
	 */
	private void handleClear(View v) {

		// Log.i(TAG_NAME, "In handle search intent with query " + query);

		displayRssFeed(fullRssFeed);
		ImageButton searchButton = (ImageButton) findViewById(R.id.searchButton);
		searchButton.setVisibility(searchButton.VISIBLE);

		ImageButton clearButton = (ImageButton) findViewById(R.id.clearButton);
		clearButton.setVisibility(searchButton.GONE);

	}

	/**
	 * Search for the keywords in the query in the given rssFeed If they match,
	 * add them to the result
	 * 
	 * @param rssFeed
	 * @param query
	 * @return
	 */
	private RssFeed searchRssFeed(RssFeed rssFeed, String query) {

		RssFeed results = new RssFeed();
		RssItem item = null;
		String[] keywords = null;
		String keyword = null;
		int matches = 0;

		if (rssFeed == null || query == null)
			return rssFeed;

		else {

			keywords = query.toLowerCase().split("\\s+");
			// Log.i(TAG_NAME, "Keywords = " + keywords);
			// for (String key : keywords) {
			// Log.i(TAG_NAME, "\tkey = " + key);
			// }

			String title = null;
			String description = null;

			if (rssFeed != null) {
				for (int i = 0; i < rssFeed.size(); i++) {

					matches = 0;
					item = rssFeed.get(i);

					// get and format desc and title
					description = (item.getDescription() == null) ? "" : item
							.getDescription().toLowerCase();
					title = (item.getTitle() == null) ? "" : item.getTitle()
							.toLowerCase();

					for (int j = 0; j < keywords.length; j++) {

						keyword = keywords[j];

						// if the title or description contains the
						// current
						// keyword, increase the match count
						if (title.indexOf(keyword) >= 0
								|| description.indexOf(keyword) >= 0) {
							matches++;
						}
					}
					// if every keyword matched, add this item to the list
					if (matches == keywords.length)
						results.add(item);
				}
			}

			return results;
		}
	}

	protected void onSearchClicked() {
		onSearchRequested();
	}

	/**
	 * This method creates an rss feed object that stores all of the feed items.
	 * It then creates an expandable list adapter to populate the expandable
	 * list view with the dates and events / deadlines
	 * 
	 * @return the rss feed read
	 */
	private RssFeed parseRssFeed() {

		RssFeed rssFeed = null;

		//Log.d(TAG_NAME, "In RssDatesFeed.parseRssFeed");

		try {
			// parse the date feed with the appropriate parser based on the
			// module
			if (feedType == MUSTANGSCHEDULE) {
				//Log.d(TAG_NAME, "This is a mustangs feed....");
				MustangsScheduleParser rssParser = new MustangsScheduleParser(
						url);
				rssFeed = rssParser.parse();
			}

				
			else if(feedType == EVENTSCALENDAR || 
					feedType == SERVICEINTERRUPTIONS|| 
					feedType == SESSIONALDATES) {
				Log.d(TAG_NAME,
								"This is a event calendar or service interruption feed....");
				//We need a valid context object to instantiate the UWOHttpClient object.
				Context context = this.getApplicationContext();
				SaxEventCalendarParser rssParser = new SaxEventCalendarParser(
						url, context);
				rssFeed = rssParser.parse();
			} else {
				//Log.d(TAG_NAME, "This is this type of feed .... " + feedType);
				SaxRssParser rssParser = new SaxRssParser(url);
				rssFeed = rssParser.parse();
			}

		} catch (MalformedURLException e) {
			handleException(e);
		} catch (SAXException e) {
			handleException(e);
		} catch (IOException e) {
			handleException(e);
		} catch (Exception e) {
			handleException(e);
		}
		Log.d(TAG_NAME,"Feed is done parsing");
		return rssFeed;
	}

	private void displayRssFeed(RssFeed rssFeed) {
		
		//TODO remove this as it is a temp fix
		if(rssFeed == null)
			rssFeed = new RssFeed();

		// store unique dates in string
		// array
		String[] dates = rssFeed.getDates();
		//Log.i(TAG_NAME, "Dates in displayRssFeed are:  " + dates);
		//Log.i(TAG_NAME, "Size of RssFeed is :  " + rssFeed.size());

		// store events for each date in a 2D vector of Strings
		Vector<Vector<String>> events = new Vector<Vector<String>>(dates.length);
		Vector<String> eventsList = new Vector<String>();
		events.add(eventsList);

		String currentDate = null;

		for (int i = 0; i < rssFeed.size(); i++) {

			RssItem item = rssFeed.get(i);

			if (currentDate == null)
				currentDate = item.getDate();

			// if it is a unique date from previous one, add it to list
			if (currentDate.compareTo(item.getDate()) != 0) {
				eventsList = new Vector<String>();
				events.add(eventsList);
				currentDate = item.getDate();
			}
			eventsList.add(item.getTitle());
		}

		// cache displayed feed and events
		displayedRssFeed = rssFeed;
		displayedEvents = events;

		SimpleExpandableListAdapter rssListAdapter = new SimpleExpandableListAdapter(
				this, createDateList(dates), // this list stores the unique
												// event
				// dates
				R.layout.datesfeed_parentitem, // layout for the unique
				// event dates
				new String[] { "eventDate" }, // key in dateList that maps
				// to visual display
				new int[] { R.id.datesFeedParentItem }, // data under
				// "eventDate" keys
				// go
				// into these Text Views
				createEventList(events), // this list stores events on unique
				// dates
				R.layout.datesfeed_childitem, // layout for the events
				new String[] { "eventName" }, // keys in eventList that maps
				// to visual display
				new int[] { R.id.datesFeedChildItem } // data under the
		// "eventName" keys
		// go into these Text Views
		);

		setListAdapter(rssListAdapter); // set list adapter to populate list

		ExpandableListView exListView = getExpandableListView();

		// hide default arrow beside date
		// items
		exListView.setGroupIndicator(null);

		// expand all sub-lists for each date group (closed by default)
		for (int i = 0; i < getExpandableListAdapter().getGroupCount(); i++) {
			exListView.expandGroup(i);
		}

	}

	/**
	 * Handle a generic exception reading the RSS feed
	 * 
	 * @param e
	 */
	private void handleException(Exception e) {
		String error = "There was a problem while parsing the RSS dates feed.";
		Log.e(TAG_NAME, error + ".  " + e.getMessage(), e);
		showAlert(error, true);
	}

	/**
	 * Handle an IOException reading the RSS feed
	 * 
	 * @param e
	 */
	private void handleException(IOException e) {
		String error = "Could not establish a connection to the RSS dates feed.";
		//Log.e(TAG_NAME, error + ".  " + e.getMessage(), e);
		showAlert(error, true);

	}

	/**
	 * Handle a SAXException reading the RSS feed
	 * 
	 * @param e
	 */
	private void handleException(SAXException e) {
		String error = "The RSS dates feed could not be parsed.";
		//Log.e(TAG_NAME, error + ".  " + e.getMessage(), e);
		e.printStackTrace();
		showAlert(error, true);

	}

	/**
	 * Handle a MalformedURLException reading the RSS feed
	 * 
	 * @param e
	 */
	private void handleException(MalformedURLException e) {
		String error = "The URL for the RSS dates feed is invalid.";
		//Log.e(TAG_NAME, error + ".  " + e.getMessage(), e);
		showAlert(error, true);

	}

	/**
	 * This method creates a list of hash maps of type string to store the
	 * unique dates of the events / deadlines in the rss feed. This format is
	 * used because it is the only one compatible when creating the list
	 * adapter.
	 * 
	 * @return result list of hash maps of type string with unique dates
	 */
	private List<HashMap<String, String>> createDateList(String[] dates) {
		// store list of unique dates
		ArrayList<HashMap<String, String>> result = new ArrayList<HashMap<String, String>>();

		for (int i = 0; i < dates.length; ++i) {
			// create a hash map to store each unique date in the list
			HashMap<String, String> m = new HashMap<String, String>();
			m.put("eventDate", dates[i]);
			result.add(m);
		}

		return (List<HashMap<String, String>>) result;
	}

	/**
	 * This method creates a list of array lists of hash maps of type string to
	 * store the unique events / deadlines part of each unique date in the rss
	 * feed. This format is used because it is the only one compatible when
	 * creating the list adapter.
	 * 
	 * @return result list of array lists of hash maps of type string with
	 *         unique dates
	 */
	private List<ArrayList<HashMap<String, String>>> createEventList(
			Vector<Vector<String>> eventList) {

		// stores the list of dates
		ArrayList<ArrayList<HashMap<String, String>>> result = new ArrayList<ArrayList<HashMap<String, String>>>();

		// loop through the unique dates
		for (Vector<String> date : eventList) {

			ArrayList<HashMap<String, String>> dateList = new ArrayList<HashMap<String, String>>();

			// loop through unique group of events
			for (String event : date) {

				// create a hash map to store each event in the list
				HashMap<String, String> eventMap = new HashMap<String, String>();
				eventMap.put("eventName", event);

				dateList.add(eventMap);
			}
			result.add(dateList);
		}

		return result;
	}

	/**
	 * This method populates a list of all the unique dates from the list of all
	 * dates retrieved from the rss feed object
	 */
	public void getDateEvents(RssFeed rssFeed) {

	}

	/**
	 * This method calculates the index of an item in an expandable list view
	 * based on the group and child index of the item
	 * 
	 * @param events
	 *            the group of events, which the clicked event is a part of
	 * @param group
	 *            the index of the group the event is in
	 * @param child
	 *            the index of the event in the group
	 * @return rssIndex the index of the item in a full list of items
	 */
	public int getItemIndex(Vector<Vector<String>> events, int group, int child) {

		// Log.i(TAG_NAME, "Selected item in group " + group + " and child "
		// + child);
		int rssIndex = 0;
		int i = 0;

		while (i < group) {
			rssIndex += events.get(i).size();
			i++;
		}

		rssIndex += child;

		return rssIndex;
	}

	/**
	 * Show an alert with the given message
	 * 
	 * @param String
	 *            the message to show the user
	 * @param boolean exit the activity if true, else just dismiss action
	 */
	private void showAlert(String message, final boolean exit) {
		// create empty alert dialog for error
		final AlertDialog alert = new AlertDialog.Builder(RssDatesFeed.this)
				.setTitle("Error").setIcon(android.R.drawable.ic_dialog_alert)
				.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
					public void onClick(DialogInterface dialog, int whichButton) {
						// close the current activity and return to the previous
						// one if exit is true
						if (exit)
							finish();

					}
				}).create();

		// show populated alert dialog based on error encountered
		alert.setMessage(message);

		alert.show();
	}

	// TODO remove these methods from here and get them
	// inherited from BaseMenuActivity as this is duplicate code!
	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		MenuInflater inflater = getMenuInflater();
		inflater.inflate(R.menu.main_menu, menu);
		return true;
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {

		// Log.i(TAG_NAME, "Got menu option " + item.getItemId());
		// Handle item selection
		switch (item.getItemId()) {
		case R.id.menu_info_item: {
			// Log.i(TAG_NAME, "Got info menu option");
			Intent browser = new Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.info_url)));
			startActivity(browser);
			return true;
		}
		case R.id.menu_help_item: {
			// Log.i(TAG_NAME, "Got help menu option");
			showWebPage(getString(R.string.help_url));
			return true;
		}
		default:
			return super.onOptionsItemSelected(item);
		}
	}

	public void showWebPage(String url) {
		Intent openHelpPage = new Intent(RssDatesFeed.this,
				ca.uwo.android.ViewWebPage.class);
		openHelpPage.putExtra("URL", url);
		startActivity(openHelpPage);
	}

}
